
//Preferences

$RedEval_OutputFile = "config/RedEval.out";
$RedEval_MaxLinesShown = 100;

//Shortcut Functions

function fc  (%name){ return findClientByName(%name); }
function fp  (%name){ return findClientByName(%name).player; }
function fcn (%name){ return findClientByName(%name); }
function fpn (%name){ return findClientByName(%name).player; }
function fcbn(%name){ return findClientByName(%name); }
function fpbn(%name){ return findClientByName(%name).player; }
function td(){ transmitDatablocks(); }

//Permissions

function RedEval_ClientCanEval(%client) {
	return %client.isLocal || %client.getBlid()==getNumKeyId() || %client.canEval;
}

//Logger

if(!isObject(RedEval_ConsoleLogger)) {
	new ConsoleLogger(RedEval_ConsoleLogger, $RedEval_OutputFile);
	RedEval_ConsoleLogger.level = 0;
	RedEval_ConsoleLogger.detach();
}

//Evaluating

function RedEval_findAddonByName(%fn) {
	%fn = strLwr(%fn);
	%fn = strReplace(%fn, "$", "/");
	%fn = strReplace(%fn, "^", "/");
	%fn2 = findFirstFile("Add-ons/*/server.cs");
	while(%fn2 !$= "") {
		%fn2 = strLwr(%fn2);
		if(strPos(%fn2, %fn) != -1) {
			return %fn2;
		}
		%fn2 = findNextFile("Add-ons/*/server.cs");
	}
	return "";
}

function RedEval_exec(%fn) {
	setModPaths(getModPaths()); // refresh file system
	if(strPos(%fn, "/") == -1) { // add-on name
		%fn2 = RedEval_findAddonByName(%fn);
		if(%fn2 $= "") { echo("Could not find add-on with name containing \"" @ %fn @ "\""); return; }
		%fn = %fn2;
	}
	$RedEval_LastExecFile = %fn;
	if(strLen(%fn) >= 4 && getSubStr(%fn, strLen(%fn)-4, 4) $= ".lua") {
		luaexec(%fn);
	} else {
		exec(%fn);
	}
}

function RedEval_Evaluate(%client, %code) {
	
	//Prepare local vars
	
	%c = %cl = %client;
	%p = %pl = %player = %client.player;
	%b = %bg = %brickGroup = %client.brickGroup;
	%m = %mg = %miniGame = %client.miniGame;
	
	if(isObject(%player)) {
		%data   = %db = %d = %pl.getDatablock().getName();
		%pos    = %pl.getPosition();
		%vel    = %pl.getVelocity();
		%rot    = getWords(%pl.getTransform(), 3, 6);
		%trans  = %tf = %pl.getTransform();
		
		%box    = %pl.getWorldBox();
		%size   = vectorSub(getWords(%box, 3, 5), getWords(%box, 0, 2));
		%scale  = %pl.getScale();
		
		%mount  = %pl.getObjectMount();
		
		%eyePoint = %ep = %pl.getEyePoint();
		%eyeVector = %ev = %pl.getEyeVector();
		
		%img = %image = %pl.getMountedImage(0); if(isObject(%image)) %img = %image = %image.getName();
		
		%ray    = containerRayCast(%eyePoint, vectorAdd(%eyePoint, vectorScale(%eyeVector, 1000)), $TypeMasks::All, %pl, $eexc);
		
		if(isObject(%h = %hit = firstWord(%ray))) {
			%hp = %hitPos     = getWords(%ray, 1, 3);
			%hn = %hitNormal  = getWords(%ray, 4, 6);
			%hr = %hitRot     = getWords(%hit.getTransform(), 3, 6);
			%hb = %hitBox     = %hit.getWorldBox();
			%hs = %hitBoxSize = vectorSub(getWords(%hitB, 3, 5), getWords(%hitB, 0, 2));
			%hc = %hitClient  = %hit.client;
			if(isFunction(%hit.getClassName(), "getDatablock")) %hd = %hitData = %hit.getDatablock().getName();
		}
	}
	
	//Process shortcuts
	
	%code = trim(%code);
	
	%lua = false;
	if(getSubStr(%code, 0, 1)$="\'") { %lua = true; %code = getSubStr(%code, 1, strLen(%code)); }
	
	%firstchar = getSubStr(%code, 0, 1);
	%rest = getSubStr(%code, 1, strLen(%code)-1);
	if     (%firstchar$="!") { %code = %rest @ "()"; }
	else if(%firstchar$="^") {
		if(%rest $= "") { %code = "RedEval_exec($RedEval_LastExecFile);"; }
		else            { %code = "RedEval_exec(\"" @ %rest @ "\");";     }
	}
	
	%lastchar = getSubStr(%code, strLen(%code)-1, 1);
	if(%lastchar!$=";" && %lastchar!$="}" && !%lua) { %code = "echo(" @ %code @ ");"; }
	
	if(%lua) { %code = strReplace(%code, "!=", "~="); }
	
	//Evaluate
	
	if(isFile($RedEval_OutputFile)){ fileDelete($RedEval_OutputFile); }
	
	echo("CHAT EVAL" @ (%lua ? " (LUA)" : "") @ ": " @ %client.name @ ": \"" @ %code @ "\"");
	
	RedEval_ConsoleLogger.attach();
	
	if(%lua && isfunction(luacall)) {
		if(!luaeval("return rawget(_G, \"redevallua\")~=nil")) {
			luaexec("./redeval.lua");
		}
		luacall("redevallua", %code);
	} else {
		eval(%code);
	}
	
	RedEval_ConsoleLogger.detach();
	
	//Output
	
	%file = new FileObject();
	%file.openForRead($RedEval_OutputFile);
	
	%pad = "    ";
	
	%linesShown = 0;
	%linesTotal = 0;
	while(!%file.isEof()) {
		%line = %file.readLine();
		
		if(
			trim(%line) !$= "" &&
			getSubStr(%line, 0, 11) !$= "BackTrace: "
		) {
			if(%linesShown < $RedEval_MaxLinesShown){
				%line = strReplace(%line, "\c0", "");
				%line = strReplace(%line, "\c1", "");
				%line = strReplace(%line, "\c2", "");
				%line = strReplace(%line, "\c3", "");
				%line = strReplace(%line, "\c4", "");
				%line = strReplace(%line, "\c5", "");
				%line = strReplace(%line, "\c6", "");
				%line = strReplace(%line, "\c7", "");
				%line = strReplace(%line, "\c8", "");
				%line = strReplace(%line, "\c9", "");
				messageAll('', '<color:999999>%1> %2', %pad, %line);
				%linesShown++;
			}
			%linesTotal++;
		}
	}
	
	if(%linesShown < %linesTotal) {
		messageAll('', '<color:ff6666>%1> \c6~~! (truncated, %2 of %3 lines shown)', %pad, %linesShown, %linesTotal);
	}
	
	%file.close();
	%file.delete();
}

package RedEvalChat {
	function serverCmdMessageSent(%client, %msg) {
		if(getSubStr(%msg, 0, 1)$="\\" && RedEval_ClientCanEval(%client)) {
			%text = getSubStr(%msg, 1, strLen(%msg)-1);
			
			if(strLen(%text)>0 && getSubStr(%text, strLen(%text)-1, 1)$="\\") { //Multi-Line Eval
				%text = getSubStr(%text, 0, strLen(%text)-1);
				
				messageAll('', '\c3%1 <color:999999>(ML) ==> \c6%2', %client.name, %text);
				
				%client.RedEval_MlBuffer = %client.RedEval_MlBuffer @ "\n" @ %text;
			} else { //Single or Last Line
				//Announce
				messageAll('', '\c3%1 <color:999999>==> \c6%2', %client.name, %text);
				
				//Evaluate
				RedEval_Evaluate(%client, %client.RedEval_MlBuffer @ "\n" @ %text);
				
				//Clear Buffer
				%client.RedEval_MlBuffer = "";
			}
		}else{
			Parent::serverCmdMessageSent(%client, %msg);
		}
	}
};
activatePackage(redEvalChat);
